#include "lib/decoders/wav/pvmvoiceimaadpcmwav.h"

#include "lib/memory/pvmmemory.h"

const pvm_int PVMVoiceIMAAPDPCMWAV::ima_index_table[16] =
{
	-1, -1, -1, -1, 2, 4, 6, 8,
	-1, -1, -1, -1, 2, 4, 6, 8
}; 

const pvm_int PVMVoiceIMAAPDPCMWAV::ima_step_table[89] =
{ 
	7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 
	19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 
	50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 
	130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
	337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
	876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 
	2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
	5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 
	15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 
};

PVMVoiceIMAAPDPCMWAV* PVMVoiceIMAAPDPCMWAV::Create(PVMDataSourceWAV* dataSource, int /*samplesPerSecond*/, int /*bitsPerSample*/, int /*chanelsNum*/)
{
	PVMVoiceIMAAPDPCMWAV* voice = new PVMVoiceIMAAPDPCMWAV();

	if(!voice)
		return NULL;

	if(!voice->Init(dataSource))
	{
		delete( voice);
		voice = NULL;
	}

	return voice;
}

PVMVoiceIMAAPDPCMWAV::PVMVoiceIMAAPDPCMWAV()
{
	dataSource = NULL;
	paused = false;
	isAlive = false;
	loopCount = 0;
	loopStart = loopEnd = -1;
	curNibblesPack = 0;
	curNibblesNum = 0;
	stepIndex = 0;
	predictedValue = 0;
}

PVMVoiceIMAAPDPCMWAV::~PVMVoiceIMAAPDPCMWAV()
{
	if(dataSource)
		dataSource->Release();
	dataSource = NULL;
	paused = false;
	loopCount = 0;
	isAlive = false;
}

bool PVMVoiceIMAAPDPCMWAV::Init(PVMDataSourceWAV* source)
{
	dataSource = source;
	dataSource->AddRef();
	isAlive = true;
	return true;
}

void PVMVoiceIMAAPDPCMWAV::GetFormat(pvm_uint32 &samplesPerSecond, pvm_uint32 &bitsperSample, pvm_uint32 &chanels)
{
	samplesPerSecond = bitsperSample = chanels = 0;
	if(dataSource!=NULL)
	{
		samplesPerSecond = dataSource->formatHeader.sampleRate;
		chanels = dataSource->formatHeader.numChannels;
		bitsperSample = 16;//dataSource->formatHeader.bitsPerSample;
	}
}

pvm_uint32 PVMVoiceIMAAPDPCMWAV::GetSamplesPerSecond()
{
	if(dataSource==NULL)
		return 0;

	return dataSource->formatHeader.sampleRate;
}

pvm_uint32 PVMVoiceIMAAPDPCMWAV::GetBitsPerSample()
{
	if(dataSource==NULL)
		return 0;

	return 16;//dataSource->formatHeader.bitsPerSample;
}

pvm_uint32 PVMVoiceIMAAPDPCMWAV::GetChanelsNum()
{
	if(dataSource==NULL)
		return 0;

	return dataSource->formatHeader.numChannels;
}

void PVMVoiceIMAAPDPCMWAV::SetLoopCount(pvm_int32 loopCount)
{
	this->loopCount = loopCount;
}

pvm_int32 PVMVoiceIMAAPDPCMWAV::GetLoopCount()
{
	return loopCount;
}

void PVMVoiceIMAAPDPCMWAV::SetPause(pvm_bool pause)
{
	this->paused = pause;
}

pvm_bool PVMVoiceIMAAPDPCMWAV::SetLoopInSamples(pvm_int64 loopFrom, pvm_int64 loopTo)
{
	pvm_int64 dataSizeInSamples = dataSource->dataSize << 1;
	if(loopFrom!=-1 && loopFrom>=dataSizeInSamples ||
		loopTo!=-1 && loopTo>=dataSizeInSamples)
	{
		return false;
	}

	this->loopStart = loopFrom;
	this->loopEnd = loopTo;

	return true;
}

pvm_bool PVMVoiceIMAAPDPCMWAV::GetLoopInSamples(pvm_int64 &loopFrom, pvm_int64 &loopTo)
{
	loopFrom = this->loopStart;
	loopTo = this->loopEnd;
	return true;
}

pvm_bool PVMVoiceIMAAPDPCMWAV::GetLoopInMS(pvm_int32 &loopFrom, pvm_int32 &loopTo)
{
	int devider = GetChanelsNum()*GetSamplesPerSecond();
	loopFrom = (pvm_int32)(this->loopStart*1000/devider);
	loopTo = (pvm_int32)(this->loopEnd*1000/devider);
	return true;
}

pvm_bool PVMVoiceIMAAPDPCMWAV::GetPause()
{
	return paused;
}

pvm_uint32 PVMVoiceIMAAPDPCMWAV::FillBuffer(void *buffer, pvm_uint32 numSamples)
{
	if(loopCount==0 || isAlive==false)
		return 0;

	pvm_uint32	samplesFilled = 0;
	pvm_uint32	sampleSize = dataSource->formatHeader.bitsPerSample>>3;
	pvm_int16*	outBufferPtr = (pvm_int16*)buffer;

	while(samplesFilled<numSamples)
	{
		pvm_int nSamplesFill = numSamples - samplesFilled;
		pvm_bool forceLoopFlag = false;

		if(loopStart >=0 && loopEnd>=0 && loopCount!=1)
		{
			if(dataSource->offsetInSamples + nSamplesFill >loopStart)
			{
				nSamplesFill = (pvm_int)(loopStart - dataSource->offsetInSamples);
			}
		}

		if(nSamplesFill>0)
		{
			int i;
			for(i = 0; i < nSamplesFill; i++)
			{
				if(curNibblesNum == 0)
				{
					if(dataSource->FillBuffer(&curNibblesPack, 2) != 2)
						break;
					curNibblesNum = 2;
				}

				pvm_uint nibble = (curNibblesNum==2) ? curNibblesPack & 0xf : ((curNibblesPack >> 4) & 0xf);

				*outBufferPtr = decodeSample(nibble);
				outBufferPtr++;
				curNibblesNum--;
			}
			samplesFilled += i;
		}

		if(dataSource->offsetInSamples == loopStart && loopCount!=1)
		{
				forceLoopFlag = true;
		}

		if((samplesFilled<numSamples&&dataSource->IsDataEnd()) || forceLoopFlag)
		{
			if(loopCount>0)
				loopCount--;
			if(loopCount!=0)
			{
				if(loopEnd>=0)
				{
					if(SetPositionInSamples(loopEnd)==false)
					{
						isAlive = false;
						break;
					}
				}
				else if(SetPositionInSamples(0)==false)
				{
					isAlive = false;
					break;
				}
			}
			else
			{
				isAlive = false;
				break;
			}
		}
	}

	return samplesFilled;
}

pvm_bool PVMVoiceIMAAPDPCMWAV::IsAlive()
{
	return isAlive;
}

void PVMVoiceIMAAPDPCMWAV::DoDestroy()
{
	delete( this);
}

void PVMVoiceIMAAPDPCMWAV::OnIdle()
{

}

pvm_bool PVMVoiceIMAAPDPCMWAV::SetPositionMS(pvm_int32 position)
{
	if(!dataSource)
		return false;

	pvm_int64 positionInSamples = position*dataSource->formatHeader.sampleRate * dataSource->formatHeader.numChannels/1000;

	return SetPositionInSamples(positionInSamples);
}

pvm_int32 PVMVoiceIMAAPDPCMWAV::GetPositionMS()
{
	if(!dataSource)
		return -1;

	return dataSource->GetPositionMS();
}

pvm_bool PVMVoiceIMAAPDPCMWAV::GetOption(const pvm_char* /*optionName*/, void* /*optionVal*/, pvm_uint32* /*optionSize*/)
{
	return false;
}

pvm_bool PVMVoiceIMAAPDPCMWAV::SetOption(const pvm_char* /*optionName*/, void* /*optionVal*/, pvm_uint32 /*optionSize*/)
{
	return false;
}

pvm_bool PVMVoiceIMAAPDPCMWAV::SetPositionInSamples(pvm_int64 position)
{
	if(!dataSource->SeekAtStart())
		return false;

	curNibblesPack = 0;
	curNibblesNum = 0;
	stepIndex = 0;
	predictedValue = 0;

	if(position > 0)
	{
		for(int i = 0; i < position; i++)
		{
			if(curNibblesNum == 0)
			{
				if(dataSource->FillBuffer(&curNibblesPack, 2)!=2)
					return false;
				curNibblesNum = 2;
			}

			pvm_uint nibble = (curNibblesNum==2) ? curNibblesPack & 0xf : ((curNibblesPack >> 4) & 0xf);

			decodeSample(nibble);
		}
	}

	return true;
}